#include "myStdio.h"

_FILE* fopen_(const char * path_name,const char *mode)
{
  int flags = 0;
  int defaultMode = 0666;

  // 以只读的方式打开文件
  if(strcmp(mode,"r") == 0)
  {
    flags |= O_RDONLY;
  }
  // 以只写的方式打开文件
  else if(strcmp(mode,"w") == 0)
  {
    flags |= (O_WRONLY | O_CREAT | O_TRUNC);
  }
  else if(strcmp(mode,"a") == 0)
  {
    flags |= (O_WRONLY | O_CREAT | O_APPEND);
  }
  else 
  {
    // 目前就简单实现这3种文件操作
  }
  // 文件描述符
  int fd = 0;
  // 以只读的方式打开文件
  if(flags & O_RDONLY) fd = open(path_name,flags);
  // 写入文件 若文件不存在 需要以defaultMode的权限创建
  else fd = open(path_name,flags,defaultMode);

  // 文件打开失败
  if(fd < 0)
  {
    // 记录下错误信息
    const char* err = strerror(errno);
    // 将错误信息写入到标准错误(2/stderr)当中
    write(2,err,strlen(err));
    // 这也就是为啥文件打开失败要返回NULL(C语言底层就是这样实现的)
    return NULL;
  }

  // 下面就是文件打开成功
  // 在堆上申请空间
  _FILE * fp = (_FILE*)malloc(sizeof(_FILE));
  // 暴力检查 未申请成功直接报断言错误
  assert(fp);

  // 默认设置为行刷新
  fp->flags = SYNC_LINE;
  // 文件描述符置为fd
  fp->fileno = fd;
  fp->capacity = SIZE;
  fp->size = 0;
  // 将缓冲区数据置为0 保证后续往缓冲区写入数据正确
  memset(fp->buffer,0,SIZE);

  // 这也就是为啥打开文件要返回FILE*的指针(C语言底层的实现方式)
  return fp;
}

void fwrite_(const char * ptr,int num,_FILE *fp)
{
  // 将数据写入到缓冲区
  // 这里的fp->buffer+fp->size 若缓冲区当中存在数据 往后追加
  // 这里不考虑缓冲区溢出的问题
  memcpy(fp->buffer+fp->size,ptr,num);
  // 缓冲区数据增加num个字节
  fp->size += num;

  // 判断刷新方式
  // 无刷新
  if(fp->flags & SYNC_NOW)
  {
    // 将缓冲区数据写入文件
    write(fp->fileno,fp->buffer,fp->size);
    // 将缓冲区置为0 惰性清空缓冲区
    fp->size = 0;
  }
  // 全刷新
  else if(fp->flags & SYNC_FULL)
  {
    //当缓冲区满了才刷新
    if(fp->size == fp->capacity)
    {
      write(fp->fileno,fp->buffer,fp->size);
      fp->size = 0;
    }
  }
  // 行缓冲
  else if(fp->flags & SYNC_LINE)
  {
    //当最后1个字符为\n时，刷新数据
    //这里不考虑 "abcd\nefg" 这种情况
    if(fp->buffer[fp->size-1] == '\n')
    {
      write(fp->fileno,fp->buffer,fp->size);
      fp->size = 0;
    }
  }
  else 
  {
    // 不执行任何操作
  }
}

void fflush_(_FILE *fp)
{
  //若缓冲区内存在数据 将缓冲区的数据写入对应的文件描述符(可能是磁盘文件，也可能是显示器)
  if(fp->size > 0)
    write(fp->fileno,fp->buffer,fp->size);
  // 强制要求操作系统对外设进行刷新
  fsync(fp->fileno);

  // 刷新完 将size置为0 表示此时缓冲区内无数据
  fp->size = 0;
}

void fclose_(_FILE *fp)
{
  // 文件关闭前要进行数据的强制刷新
  fflush_(fp);
  // 关闭对应的文件描述符
  // 文件描述指向的就是文件(关闭文件)
  close(fp->fileno);
}
